# 与项目相关的序列化器

from sa.models import Project, Activity
from rest_framework import serializers
# 为了显示客户对象
from sa.serializers.customerSerializer import CustomerInfoSerializer
# 为了显示用户对象
from sa.serializers.accountSerializer import UserInfoSerializer

import re
from django.utils import timezone
from sa.models import User, CustomerInfo


class ProjectInfoSerializer(serializers.ModelSerializer):
    # 用于读取的嵌套序列化器 为了获取关联字段是对象，不只是id
    customer_info = CustomerInfoSerializer(source='customer', read_only=True)
    creator_info = UserInfoSerializer(source='creator', read_only=True)
    participants_info = UserInfoSerializer(
        source='participants', many=True, read_only=True)

    # 用于写入的字段，仅接收ID
    customer = serializers.PrimaryKeyRelatedField(
        queryset=CustomerInfo.objects.all(), write_only=True)
    creator = serializers.PrimaryKeyRelatedField(
        queryset=User.objects.all(), write_only=True, required=False)
    participants = serializers.PrimaryKeyRelatedField(
        queryset=User.objects.all(), many=True, write_only=True, required=False)

    # choices 字段显示 display
    # get_字段名_display 是一个由 choices 参数自动生成的模型方法，
    # 用于获取字段的可读显示名称（display label）而不是数据库中存储的值。
    software_display = serializers.CharField(
        source='get_software_display',  # 调用模型方法
        read_only=True)
    level_display = serializers.CharField(source='get_level_display',
                                          read_only=True)
    status_display = serializers.CharField(source='get_status_display',
                                           read_only=True)

    class Meta:
        model = Project
        fields = "__all__"
        read_only_fields = ['id', 'duration', 'points']
        # extra_kwargs = {
        #     'mfd_num': {
        #         'required': True,
        #         'allow_null': False,
        #         'min_value': 0
        #     },
        #     'contract_amount ': {
        #         'required': True,
        #         'allow_null': False,
        #         'min_value': 0
        #     }
        # }

    def validate_SR_No(self, value):
        # 验证项目编号必须是不以0开头的正整数
        if not re.match(r'^[1-9]\d{8}$', value):
            raise serializers.ValidationError('必须是非0开头的9位正整数！')
        return value

    def validate(self, attrs):
        # 验证 participants 参与者
        # DRF 的序列化器允许你通过 context 参数传递额外的数据, context是个字典
        # DRF 的视图类（如 APIView、GenericAPIView 和视图集 ViewSet）会自动将 request 对象添加到序列化器的上下文中
        # DRF 会自动将 request 对象添加到序列化器的上下文中。
        # print("Attrs before validation:", attrs)
        # 获取当前登录用户
        request = self.context.get('request')  # 从上下文中获取请求对象
        if not request or not request.user.is_authenticated:
            raise serializers.ValidationError('需要登录用户')

        current_user = request.user
        # 验证 is_team_project 和 participants
        team_project = attrs.get("is_team_project", False)
        # 获取已选择的参与者（可能为 None 或空列表）
        participants = attrs.get('participants', [])
        """
        isinstance() 是 Python 内置的类型检查函数，用于判断一个对象是否属于指定的类型（或类型的元组）
        语法：isinstance(object, type_or_tuple)
            object：要检查的对象
            type_or_tuple：类型或类型元组（可检查多种类型）
            返回值：True（对象是指定类型）或 False
        """
        # 如果 participants 不是 list 类型，条件 not isinstance(participants, list) 将为 True。
        if not isinstance(participants, list):
            participants = []  # 确保 participants 是一个列表

        # 1. 创建一个新表，统一转换为ID列表，因为前端提交的是对象 [<User: 郑琦>]
        participant_ids = []
        for p in participants:
            # User 是 Django 框架默认提供的用户模型类，
            # 属于 django.contrib.auth.models 模块。
            # 它是 Django 内置认证系统（Authentication System）的核心组成部分，
            # 用于管理用户账户、权限和会话
            if isinstance(p, User):  # # 检查是否是User对象  [<User: 郑琦>]
                # 如果p是User对象，取p.id
                participant_ids.append(p.id)
            elif isinstance(p, int):  # 检查是否是整数ID
                participant_ids.append(p)
            # 忽略其他非法类型
        # 验证团队项目必须有其他参与者
        if team_project:
            # 2. 先检查是否已有其他参与者（排除当前用户
            # 列表推导式：只有当 pid 不等于 current_user.id 时，pid 才会被包含在 other_participants 列表中
            other_participants = [pid for pid in participant_ids if pid != current_user.id]
            if not other_participants:  # 如果没有其他参与者，返回空列表
                raise serializers.ValidationError({
                    'participants': '团队项目必须包含至少一个其他参与者'
                })
            # 3. 只有满足条件后才添加当前用户（如果尚未在列表中）
            if current_user.id not in participant_ids:
                participant_ids.append(current_user.id)
        # 更新 attrs 中的 participants
        attrs['participants'] = participant_ids

        # 验证工作分类
        category = attrs.get('category')
        level = attrs.get('level')
        if not category or category == '--- 请选择分类 ---':
            raise serializers.ValidationError({
                'category': '请选择工作分类！'
            })
        if category == '故障排除' and not level:
            raise serializers.ValidationError({
                'level': '需要选择level！'
            })

        # 验证日期逻辑并自动计算duration
        today = timezone.now().date()
        start_date = attrs.get('start_date')
        status = attrs.get('status', 'Ongoing')
        end_date = attrs.get('end_date')
        if not start_date:
            raise serializers.ValidationError({'start_date': '必须提供开始日期！'})
        if status == 'Completed':
            if not end_date:
                raise serializers.ValidationError({'end_date': '必须提供结束日期'})
            if end_date > today:
                raise serializers.ValidationError({'end_date': '结束日期不能是未来日期'})
            if end_date < start_date:
                raise serializers.ValidationError({'end_date': '结束日期不能早于开始日期'})
        if start_date and end_date:
            # 更新数据 两个日期相减结果是一个对象，需要.days
            attrs['duration'] = (end_date - start_date).days + 1

        # 验证大项目
        big_project = attrs.get('big_project', False)
        contract_amount = attrs.get('contract_amount', 0)
        mfd_num = attrs.get('mfd_num', 0)
        server_num = attrs.get('server_num', 0)

        """
        self.initial_data 是 Django REST Framework (DRF) 序列化器中的一个属性，
        它包含了用户提交的原始数据（通常是请求体中的数据）。
        这个属性在序列化器的验证过程中非常有用，尤其是在需要判断用户是否显式提供了某个字段时。
        原始数据来源：
            self.initial_data 是用户通过请求（如 POST、PUT 请求）提交到 API 的原始数据。
            它通常是一个字典，包含了请求体中的所有字段及其对应的值。
        与 attrs 的区别：
            attrs 是在序列化器的 validate 方法中传递的参数，它包含了经过初步处理的数据。
            attrs 中的数据可能已经经过字段级别的验证，并且某些字段可能被设置了默认值（如果模型字段定义了默认值）。
            相比之下，self.initial_data 保留了用户提交的原始数据，没有经过任何处理或修改。
        用途：
            由于 attrs 中的数据可能已经经过处理，某些字段可能不再反映用户实际提交的值（例如，被设置了默认值）。
            因此，当你需要判断用户是否显式提供了某个字段时，应该使用 self.initial_data 而不是 attrs。
        """

        if big_project is True:
            #  因为模型 default=0, 所以永远不会等于 None.
            #  通过 self.initial_data 来判断用户是否输入提交数据
            if 'mfd_num' not in self.initial_data or 'contract_amount' not in self.initial_data:
                raise serializers.ValidationError({
                    'mfd_num': '提供数据不能为空或无效值',
                    'contract_amount': '提供数据不能为空或无效值',
                })

            if mfd_num <= 0 and contract_amount <= 0:
                raise serializers.ValidationError({
                    'mfd_num': '大项目MFD数量和软件金额不能同时为0。',
                    'contract_amount': '大项目MFD数量和软件金额不能同时为0。',
                })

            # 检查mfd_num 和 contract_amount 关系
            if contract_amount < 100 and mfd_num < 10:
                raise serializers.ValidationError({
                    'mfd_num': 'MFD < 10台，软件金额必须 ≥ 100K。',
                    'contract_amount': '软件金额 < 100K, MFD必须 ≥ 10台。',
                })

        # 验证 server_num, mfd_num, printer_num 不能空值不提供
        if 'server_num' not in self.initial_data:
            raise serializers.ValidationError({'server_num': '请提供有效值或0'})
        if 'mfd_num' not in self.initial_data:
            raise serializers.ValidationError({'mfd_num': '请提供有效值或0'})
        if 'printer_num' not in self.initial_data:
            raise serializers.ValidationError({'printer_num': '请提供有效值或0'})

        # 验证software
        software = attrs.get('software')
        if not software:
            raise serializers.ValidationError({
                'software': '需要选择有效的解决方案'
            })

        """
        self.instance
        :是 DRF 序列化器中的一个重要属性，它表示当前序列化器正在处理的模型实例。
        当创建新对象时：self.instance 为 None
        当更新现有对象时：self.instance 包含正在更新的模型实例
        典型使用场景：在验证逻辑中区分创建和更新操作,在保存逻辑中访问现有对象的数据
        """

        # 验证项目状态：当项目没有activity时，状态不能是 Completed
        status = attrs.get('status')
        # self.instance 不为 None时，就是更新
        if self.instance and self.instance.pk:
            # 检查项目是否有任何activity
            has_activities = Activity.objects.filter(project=self.instance).exists()
            if status == 'Completed' and not has_activities:
                raise serializers.ValidationError({
                    'status': '没有activity,不能标记为Completed'
                })
        else:
            # 对于新建项目，默认不允许直接设置为Completed
            if status == 'Completed':
                raise serializers.ValidationError({
                    'status': '新建项目不能直接标记为Completed，请继续添加activity'
                })

        # 验证项目开始日期不能晚于所有activity中的日期
        if self.instance and start_date:
            try:
                latest_activity = self.instance.activity_set.latest('activity_date')
                if (latest_activity and
                        latest_activity.activity_date and
                        start_date > latest_activity.activity_date):
                    raise serializers.ValidationError({
                        'start_date': '开始日期不能晚于任务日期'
                    })
                else:
                    pass
            except Activity.DoesNotExist:
                # 如果没有activity，则不需要验证start_date与activity的关系
                pass

        # 验证项目完成日期不能早于所有activity中的日期（仅当状态为"Completed"时）
        if self.instance and status == 'Completed' and end_date:
            # 按 activity_date 最大值的 activity
            latest_activity = self.instance.activity_set.latest('activity_date')
            if (latest_activity and
                    latest_activity.activity_date and
                    end_date < latest_activity.activity_date):
                raise serializers.ValidationError({
                    'end_date': '结束日期不能早于任务日期'
                })

        # 调试信息 提交的数据
        # print("Initial data:", self.initial_data)
        # 调试信息 完成校验后的数据
        # print("Attrs after validation:", attrs)

        return attrs
